package emu.decvices.cpu;

import emu.decvices.bus.BusDevice;
import emu.decvices.bus.IRQLine;
import emu.decvices.clock.ClockDevice;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * 6502CPU模拟器
 */
public class CPU extends BusDevice implements ClockDevice {

    public CPU() {
        initInstructionList();
    }

    /**
     * 状态寄存器的8个标志位常量
     */
    /**
     * 进位标志(Carry Flag)
     * 如果上次操作导致结果的第 7 位溢出或第 0 位下溢，则设置进位标志。在算术运算、
     * 比较和逻辑移位时都会设置该条件。可以使用 "设置进位标志"（SEC）指令明确设置该标志，
     * 也可以使用 "清除进位标志"（CLC）指令清除该标志。
     */
    public static final int FLAG_C = 1 << 0;
    /**
     * 零标志(Zero Flag)
     * 如果上一次操作的结果为零，则设置零标志。
     */
    public static final int FLAG_Z = 1 << 1;
    /**
     * 中断禁用(Interrupt Disable)
     * 如果程序执行了 "设置中断禁用"（SEI）指令，则中断禁用标志被置位。
     * 当该标志被设置时，处理器将不响应来自设备的中断，直到 "清除中断禁用"（CLI）指令将其清除。
     */
    public static final int FLAG_I = 1 << 2;//是禁止中断标志位（disable interrupt），值设定为1后就不允许中断程序。
    /**
     * 十进制模式(Decimal Mode)
     * 当十进制模式标志被设置时，处理器将在加减运算过程中遵守二进制编码十进制（BCD）算术规则。
     * 可以使用 "设置十进制标志"（SED）明确设置该标志，也可以使用 "清除十进制标志"（CLD）清除该标志。
     */
    public static final int FLAG_D = 1 << 3;//是十进制模式位（decimal mode），我要说明的是现在我们的6502模拟器不需要使用十进制模式，所以这个标志位基本是多余的。NES使用了6502微处理器的一个修改版本，它的硬件没有配置十进制模式，所以现在我决定不模拟十进制模式，在未来我再考虑是否回来部署十进制模式，所以现在这就是一个真实的6502模拟器（没有十进制模式，但是预设了该功能）。
    /**
     * 中断命令(Break Command)
     * 当执行 BRK 指令并为处理该指令而产生中断时，中断命令位被置位。
     */
    public static final int FLAG_B = 1 << 4;//是中断标志位（break），值是1的话表示调用过中断。
    public static final int FLAG_U = 1 << 5;//是一个未使用的标志位。
    /**
     * 溢出标志位(Overflow Flag)
     * 在算术运算过程中，如果运算结果是无效的 2 的补码结果（例如，与正数相加，结果为负数：64 + 64 => -128），则溢出标志位被置位。
     * 判断方法是查看第 6 位和第 7 位之间以及第 7 位和进位标志之间的进位。
     */
    public static final int FLAG_V = 1 << 6;//是溢出标志位（overflow）
    /**
     * 负标志(Negative Flag)
     * 如果最后一次运算的结果第 7 位被设置为 1，则负标志被设置。
     */
    public static final int FLAG_N = 1 << 7;//是负数标志位（negative），在程序员想要使用6502运算有符号变量的情况下，就会用到这2个标志位

    /**
     * 寻址模式名称
     */
    public static final String AM_IMP = "AM_IMP";
    public static final String AM_IMM = "AM_IMM";
    public static final String AM_ZP0 = "AM_ZP0";
    public static final String AM_ZPX = "AM_ZPX";
    public static final String AM_ZPY = "AM_ZPY";
    public static final String AM_REL = "AM_REL";
    public static final String AM_ABS = "AM_ABS";
    public static final String AM_ABX = "AM_ABX";
    public static final String AM_ABY = "AM_ABY";
    public static final String AM_IND = "AM_IND";
    public static final String AM_IZX = "AM_IZX";
    public static final String AM_IZY = "AM_IZY";
    public static final String AM_ACC = "AM_ACC";

    /**
     * 6中寄存器
     */
    private int a;//A寄存器 Accumulator Register
    private int x;//X寄存器
    private int y;//Y寄存器
    private int stack_point;//栈指针寄存器
    private int pc;//程序计数器寄存器
    private int status;//状态寄存器，对应上述FLAG_X常量


    public int fetch() {
        return fromBusRead(address_abs);
    }

    //当前命令使用的寻址模式
    private String addressModeName;
    /*
        我们可能会依据不同的寻址方式，从内存的各种地址读取数据，
        所以我把这个地址存放在这个16位的变量中（address_abs）。
        在6502中，分支指令让程序指针从被调用的地址跳转另一个地址的时候，不能超过特定的距离，而且它们以相对距离的方式跳转。
        我会用一个变量（opcode）来存储当前操作的指令操作码，用另一个变量（cycles）来存储当前指令持续时间内剩余的时钟周期数。
     */
    private int address_abs;
    private int address_rel;
    private int opcode;
    private int cycles;

    /**
     * 获取CPU状态
     *
     * @return
     */
    public int getStatus(int flag) {
        return status & flag;
    }


    /**
     * 设置指定状态的值
     *
     * @param flag_x 对应FLAG_X常量
     * @param v      当v为true时状态对应位为1，否则为0
     *               <p>
     *               例如:
     *               setStatus(FLAG_Z,true)
     *               那么:
     *               static的2进制内容为:00000010
     *               ^
     *               对应【零标志位zero bit】
     */
    public void setStatus(int flag_x, boolean v) {
        if (v) {
            status |= flag_x;
        } else {
            status &= ~flag_x;
            status &= 0x00FF;
        }
    }

    //处理每一次时钟
    public void clock() {
        if (cycles == 0) {
//            System.out.println("pc:"+pc);
            opcode = fromBusRead(pc);

            pc++;
            Instruction instruction = instructionList.get(opcode);
            cycles = instruction.cycles;//获取该条指令消耗的CPU时钟周期
            byte cycle1 = instruction.addressMode.execute();//执行寻址模式
            addressModeName = instruction.addressModeName;//设置当前指令的寻址模式名称
//            System.out.println(instruction.name+"()  "+addressModeName + "  address_abs:"+address_abs + "   address_rel:"+address_rel);
            byte cycle2 = instruction.operate.execute();//执行该指令
            cycles += (cycle1 & cycle2);//如果寻址模式和执行命令需要都需要增加时钟，则时钟累加

            if (IRQLine.getInstance().needIrq()) { //是否需要处理中断
                if (irqDelayed) {
                    irqDelayed = false;
                } else {
                    irq();
                }
            }
        }
        cycles--;
    }

    /*
        CPU支持的三种中断
        note:
        在6502中还有3种输入信号必须要模拟：
        第1个是复位信号（reset signal），
        第2个是中断请求信号（interrupt request signal），
        第3个是不可屏蔽的中断请求信号（non-maskable interrupt request signal）。

        这3个输入信号可以在任何时候生效。它们需要以异步方式（asynchronously，异步就是不按照代码的顺序执行，
        用白话讲即很多事情可以一起并排着去做，不同于同步那样，非要等上一件事情做完了才能去做下一件事情。）来执行，
        并且它们可以中断处理器正在执行的任务。不过它仍然会完成当前正在执行的指令，这取决于中断的类型和处理器的各种变化情况

        如果“禁止中断标志位(FLAG_I)”设置为1，那么标准的中断请求指令（irq）就会被无视。“不可屏蔽中断”（nmi）永远不能被禁止中断。
     */
    public void reset() {
        a = 0;
        x = 0;
        y = 0;
        stack_point = 0xFD;
        status = 0 | FLAG_U;
        address_abs = 0xFFFC;
        int low = fromBusRead(address_abs);
        int hi = fromBusRead(address_abs + 1);
        pc = (hi << 8) | low;
        address_rel = 0;
        address_abs = 0;
        cycles = 8;//复位需要8个时钟周期

    }//复位操作，应该就是FC上的reset按键按下后的操作

    /**
     * IRQ（中断请求）是 NES CPU 上的一个信号。它用于触发 CPU 中断。
     * <p>
     * 如果 CPU 的 /IRQ 输入在指令结束时为 0，则 CPU 会推送程序计数器和处理器状态寄存器，
     * 设置 I 标志以忽略其他 IRQ，并且程序计数器会读取 $fffe 和 $ffff 的值。
     * <p>
     * CPU 的中断禁用状态标志掩盖了这种行为。SEI 指令禁用 IRQ，CLI 指令启用 IRQ。
     * <p>
     * /IRQ 处理程序应推送其使用的任何寄存器，通过写入端口确认中断，使中断源不再将 /IRQ 强制为 0，
     * 然后拉回寄存器，并返回 RTI。
     * <p>
     * 因此，如果程序使用多个 IRQ 源，则应通过软件处理相互冲突的中断之间的优先级。
     * <p>
     * <p>
     * <p>
     * CLI、SEI 和 PLP 后的延迟 IRQ 响应
     * RTI 指令会立即影响 IRQ 抑制。如果 IRQ 挂起并执行了清除 I 标志的 RTI，CPU 将在 RTI 执行完毕后立即调用 IRQ
     * 处理程序。这是由于 RTI 在轮询中断之前从堆栈中恢复了 I 标志。
     * <p>
     * 另一方面，CLI、SEI 和 PLP 指令会在轮询中断后更改 I 标志（与所有双周期指令一样，它们会在第一个周期结束时轮询中
     * 断线路），这意味着它们可以有效地将中断延迟到下一条指令之后。例如，如果一个中断正在等待处理，而 I 标志当前处于设
     * 置状态，那么执行 CLI 指令将在 CPU 调用 IRQ 处理程序之前执行下一条指令。
     * <p>
     * 可以通过测试 ROM 映像在仿真器中验证和测试这种行为。
     */
    public void irq() {
        if (getStatus(FLAG_I) == FLAG_I) {
            return;
        }
        //System.out.println("irq!!!");
        push(pc >> 8);
        push(pc);
        setStatus(FLAG_B, false);
        push(status);
        setStatus(FLAG_I, true);

        address_abs = 0xFFFE;//中断需要的指令地址其实内存位置，固定的
        int low = fromBusRead(address_abs);
        int hi = fromBusRead(address_abs + 1);
        pc = (hi << 8) | low;//连读读取的两个字节为实际地址，赋值给程序计数器
        cycles = 7;

    }//中断请求信号(interrupt request signal)

    public void nmi() {
        //System.out.println("nmi!!!");
        push(pc >> 8);
        push(pc);
        setStatus(FLAG_B, false);
        push(status);
        setStatus(FLAG_I, true);
        address_abs = 0xFFFA;//中断需要的指令地址其实内存位置，固定的
        int low = fromBusRead(address_abs);
        int hi = fromBusRead(address_abs + 1);
        pc = (hi << 8) | low;//连读读取的两个字节为实际地址，赋值给程序计数器
        cycles = 8;
    }//不可屏蔽的中断请求信号(non-maskable interrupt request signal)

    //以下方法为CPU指令的寻址模式，一共12种
    //------------------------------------------------------------------------------------------------------

    /**
     * 6502的IMP（Implied）寻址方式，也被称为隐含寻址，是一种特殊的寻址方式。
     * <p>
     * 在IMP寻址方式中，指令不需要操作数，因为操作数已经隐含在指令本身中。例如，指令INX表示增加X寄存器的内容，这个指令就是使用的IMP寻址方式。
     * <p>
     * 具体步骤如下：
     * <p>
     * 从PC（Program Counter）指向的地址读取指令，PC自增。
     * 执行指令。因为指令已经隐含了操作数，所以不需要额外的读取操作数的步骤。
     * 这种寻址方式的一个典型应用是在处理寄存器的操作时，可以直接使用IMP寻址方式的指令，无需额外的操作数。
     *
     * @return
     */
    public byte AM_IMP() {
        return 0;
    }

    public byte AM_ACC() {
        return 0;
    }

    /**
     * 6502的IMM（Immediate）寻址方式，也被称为立即寻址，是一种直接寻址方式。
     * <p>
     * 在IMM寻址方式中，指令的操作数直接附在指令后面，立即被指令使用，不需要从内存中读取。
     * <p>
     * 具体步骤如下：
     * <p>
     * 从PC（Program Counter）指向的地址读取操作数，PC自增。
     * 操作数直接被指令使用。
     * 这种寻址方式的一个典型应用是在需要立即数值的指令中，比如将一个立即数值加载到寄存器，或者与一个立即数值进行算术运算等。
     *
     * @return
     */
    public byte AM_IMM() {
        address_abs = pc++;
        return 0;
    }

    /**
     * 6502的ZP0（Zero Page）寻址方式，也被称为零页寻址，是一种直接寻址方式。
     * <p>
     * 在ZP0寻址方式中，指令的操作数是一个无符号8位数，这个数就是最终的操作数地址。这个地址在零页范围内，即0x0000到0x00FF。
     * <p>
     * 具体步骤如下：
     * <p>
     * 从PC（Program Counter）指向的地址读取操作数，PC自增。
     * 操作数就是最终的操作数地址。
     * 这种寻址方式的一个典型应用是在处理零页内的数据时，可以直接通过ZP0寻址方式快速访问零页内的任意位置。由于零页地址只需要一个字节，所以ZP0寻址方式比其他需要两个字节地址的寻址方式更快。
     * <p>
     * 零页
     * 使用零页寻址模式的指令只有 8 位地址操作数。这就限制了它只能寻址内存的前 256 个字节（如 $0000 至 $00FF），
     * 其中地址的最重要字节始终为 0。在零页模式下，指令中只保留地址的最小有效字节，使其缩短一个字节（对节省空间很重要），
     * 并在执行过程中减少一次内存取回（对提高速度很重要）。
     * 如果操作数求值为零页地址，并且指令支持零页寻址模式，汇编程序将自动选择零页寻址模式（并非所有汇编程序都支持这种模式）。
     * <p>
     * <p>
     * NES内存地址2字节，
     * 0xFFFF
     * ^^    表示第几页            共255页
     * ^^  表示第几个字节         每页255个字节
     *
     * @return
     */
    public byte AM_ZP0() {
        address_abs = fromBusRead(pc);//读取指令后的字节(操作数是一个地址00-FF,这里没有页数，因为是0页寻址)
        pc++;//程序计数器+1
        return 0;
    }

    /**
     * 6502的ZPX（Zero Page, X）寻址方式，也称为零页X寻址，是一种直接寻址方式。
     * <p>
     * 在ZPX寻址方式中，指令的操作数是一个无符号8位数，这个数和X寄存器的内容相加（忽略溢出）得到的结果是最终的操作数地址。这个地址在零页范围内，即0x0000到0x00FF。
     * <p>
     * 具体步骤如下：
     * <p>
     * 从PC（Program Counter）指向的地址读取操作数，PC自增。
     * 将操作数和X寄存器的内容相加（忽略溢出）得到一个地址A。
     * 地址A就是最终的操作数地址。
     * 这种寻址方式的一个典型应用是在处理零页内的数据时，可以用X寄存器作为偏移，通过ZPX寻址方式快速访问零页内的任意位置。
     * <p>
     * 零页，X
     * 使用索引零页寻址的指令所要访问的地址，是通过从指令中获取 8 位零页地址并加上 X 寄存器的当前值计算得出的。
     * 例如，如果 X 寄存器中包含 $0F，执行指令 LDA $80,X 后，累加器将从 $008F 开始加载（例如 $80 + $0F => $8F）。
     * <p>
     * 注意
     * 如果基地址和寄存器的总和超过 $FF，地址计算将绕过去。
     * 如果我们重复上一个示例，但 X 寄存器中的值为 $FF，那么累加器将从 $007F 加载（例如：$80 + $FF => $7F），而不是从 $017F 加载。
     *
     * @return
     */
    public byte AM_ZPX() {
        address_abs = (fromBusRead(pc) + x)&0xFF;
        pc++;
        return 0;
    }

    /**
     * 6502的ZPY（Zero Page, Y）寻址方式，也称为零页Y寻址，是一种直接寻址方式。
     * <p>
     * 在ZPY寻址方式中，指令的操作数是一个无符号8位数，这个数和Y寄存器的内容相加（忽略溢出）得到的结果是最终的操作数地址。这个地址在零页范围内，即0x0000到0x00FF。
     * <p>
     * 具体步骤如下：
     * <p>
     * 从PC（Program Counter）指向的地址读取操作数，PC自增。
     * 将操作数和Y寄存器的内容相加（忽略溢出）得到一个地址A。
     * 地址A就是最终的操作数地址。
     * 这种寻址方式的一个典型应用是在处理零页内的数据时，可以用Y寄存器作为偏移，通过ZPY寻址方式快速访问零页内的任意位置。
     *
     * @return
     */
    public byte AM_ZPY() {
        address_abs = (fromBusRead(pc) + y)&0xFF;
        pc++;
        return 0;
    }

    /**
     * 6502的ABS（Absolute）寻址方式，也被称为绝对寻址，是一种直接寻址方式。
     * <p>
     * 在ABS寻址方式中，指令的操作数是一个无符号16位数，这个数就是最终的操作数地址。
     * <p>
     * 具体步骤如下：
     * <p>
     * 从PC（Program Counter）指向的地址读取操作数（两个字节），PC自增2。
     * 操作数就是最终的操作数地址。
     * 这种寻址方式的一个典型应用是在处理内存中的数据时，可以直接通过ABS寻址方式访问内存中的任意位置。由于操作数是一个16位数，所以ABS寻址方式可以访问整个64KB的内存空间。
     * 绝对寻址
     * <p>
     * 使用绝对寻址的指令包含一个完整的 16 位地址，以确定目标位置。
     *
     * @return
     */
    public byte AM_ABS() {
        int low = fromBusRead(pc);
        pc++;
        int high = fromBusRead(pc);
        pc++;
        address_abs = (high << 8) | low;
        return 0;
    }

    /**
     * 6502的ABX（Absolute, X）寻址方式，也被称为绝对X寻址，是一种直接寻址方式。
     * <p>
     * 在ABX寻址方式中，指令的操作数是一个无符号16位数，这个数和X寄存器的内容相加（忽略溢出）得到的结果是最终的操作数地址。
     * <p>
     * 具体步骤如下：
     * <p>
     * 从PC（Program Counter）指向的地址读取操作数（两个字节），PC自增2。
     * 将操作数和X寄存器的内容相加（忽略溢出）得到一个地址A。
     * 地址A就是最终的操作数地址。
     * 这种寻址方式的一个典型应用是在处理内存中的数据时，可以用X寄存器作为偏移，通过ABX寻址方式快速访问内存中的任意位置。
     * <p>
     * 绝对值,X
     * 使用 X 寄存器索引的绝对寻址指令所要访问的地址，是通过指令的 16 位地址加上 X 寄存器的内容计算出来的。
     * 例如，如果 X 寄存器中包含 $92，那么 STA-2000,X 指令将把累加器存储在 $2092处（例如  $2000 +  $92）。
     *
     * @return
     */
    public byte AM_ABX() {
        AM_ABS();
        int old_page = address_abs & 0xFF00;
        address_abs = (address_abs + x) & 0xFFFF;
        if ((address_abs & 0xFF00) != old_page) {
            return 1;
        }
        return 0;
    }


    /**
     * 6502的ABY（Absolute, Y）寻址方式，也被称为绝对Y寻址，是一种直接寻址方式。
     * <p>
     * 在ABY寻址方式中，指令的操作数是一个无符号16位数，这个数和Y寄存器的内容相加（忽略溢出）得到的结果是最终的操作数地址。
     * <p>
     * 具体步骤如下：
     * <p>
     * 从PC（Program Counter）指向的地址读取操作数（两个字节），PC自增2。
     * 将操作数和Y寄存器的内容相加（忽略溢出）得到一个地址A。
     * 地址A就是最终的操作数地址。
     * 这种寻址方式的一个典型应用是在处理内存中的数据时，可以用Y寄存器作为偏移，通过ABY寻址方式快速访问内存中的任意位置。
     *
     * @return
     */
    public byte AM_ABY() {
        AM_ABS();
        int old_page = address_abs & 0xFF00;
        address_abs = (address_abs + y) & 0xFFFF;
        if ((address_abs & 0xFF00) != old_page) {
            return 1;
        }
        return 0;
    }


    /**
     * 6502的REL（Relative）寻址方式，也被称为相对寻址，是一种特殊的寻址方式，主要用于跳转指令。
     * <p>
     * 在REL寻址方式中，指令的操作数是一个有符号8位数，这个数被加到PC（Program Counter）上，得到的结果是最终的操作数地址。
     * <p>
     * 具体步骤如下：
     * <p>
     * 从PC（Program Counter）指向的地址读取操作数，PC自增。
     * 将操作数（视为有符号数）加到PC上，得到一个地址A。
     * 地址A就是最终的操作数地址。
     * 这种寻址方式的一个典型应用是在处理跳转指令时，可以用REL寻址方式快速计算跳转的目标地址。由于操作数是有符号数，所以跳转的目标地址可以在当前地址的前128字节到后127字节范围内。
     * <p>
     * <p>
     * 相对寻址
     * 相对寻址模式用于分支指令（如 BEQ、BNE 等），这些指令包含一个带符号的 8 位相对偏移量（如 -128 至 +127），
     * 如果条件为真，该偏移量将添加到程序计数器中。由于程序计数器本身会在指令执行期间递增 2，
     * 因此目标指令的有效地址范围必须在分支的 -126 至 +129 字节之间。
     *
     * @return
     */
    public byte AM_REL() {
        address_rel = fromBusRead(pc);
        pc++;
        if ((address_rel & 0x80) == 0x80) {//是否为1byte有符号表示的负数
            address_rel |= 0xFFFFFF00;
        }
        return 0;
    }

    /**
     * 6502的IND（Indirect）寻址方式，也被称为间接寻址，是一种间接寻址方式。
     * <p>
     * 在IND寻址方式中，指令的操作数是一个无符号16位数，这个数是一个地址。从这个地址处读取两个字节，这两个字节组成一个16位的数，这个数就是最终的操作数地址。
     * <p>
     * 具体步骤如下：
     * <p>
     * 从PC（Program Counter）指向的地址读取操作数（两个字节），PC自增2。
     * 从操作数指向的地址处读取一个字节，从操作数指向的地址+1处读取另一个字节，两个字节组成一个16位的地址A。
     * 地址A就是最终的操作数地址。
     * 这种寻址方式的一个典型应用是在处理间接跳转指令时，可以用IND寻址方式快速计算跳转的目标地址。
     *
     * @return
     */
    public byte AM_IND() {
        int low = fromBusRead(pc);
        pc++;
        int high = fromBusRead(pc);
        pc++;
        int pointer = (high << 8) | low;

        if (low == 0x00FF) {
            address_abs = (fromBusRead(pointer & 0xFF00) << 8) | fromBusRead(pointer);
        } else {
            address_abs = (fromBusRead(pointer + 1) << 8) | fromBusRead(pointer);
        }
        return 0;
    }

    /**
     * 6502的IZX（Indexed Indirect, X）寻址方式，也被称为X索引间接寻址，是一种间接寻址方式。
     * <p>
     * 在IZX寻址方式中，指令的操作数是一个无符号8位数，这个数和X寄存器的内容相加（忽略溢出），得到的结果是一个地址。从这个地址处读取两个字节，这两个字节组成一个16位的数，这个数就是最终的操作数地址。
     * <p>
     * 具体步骤如下：
     * <p>
     * 从PC（Program Counter）指向的地址读取操作数，PC自增。
     * 将操作数和X寄存器的内容相加（忽略溢出）得到一个地址A。
     * 从地址A处读取一个字节，从地址A+1处读取另一个字节，两个字节组成一个16位的地址B。
     * 地址B就是最终的操作数地址。
     * 这种寻址方式的一个典型应用是在处理基于结构体数组的数据时，可以将结构体数组的基地址存放在内存中，用X寄存器作为索引，通过IZX寻址方式快速访问结构体数组的任意元素。
     *
     * @return
     */
    public byte AM_IZX() {
        int d = fromBusRead(pc);
        pc++;
        int low = fromBusRead((d + x) & 0xFF);
        int high = fromBusRead((d + x + 1) & 0xFF);
        address_abs = (high << 8) | low;
        return 0;
    }

    /**
     * 6502的IZY（Indirect Y）寻址方式，也被称为后索引间接寻址。它常用于处理基于结构体数组的数据。
     * <p>
     * 在IZY寻址方式中，指令的操作数是一个无符号8位数，这个数被用来从零页获取一个地址。然后，Y寄存器的内容被添加到这个地址上（忽略溢出），得到的结果是最终的操作数地址。
     * <p>
     * 具体步骤如下：
     * <p>
     * 从PC（Program Counter）指向的地址读取操作数，PC自增。
     * 从零页地址（操作数）处读取一个字节，从零页地址+1处读取另一个字节，两个字节组成一个16位的地址A。
     * 将地址A和Y寄存器的内容相加（忽略溢出）得到一个地址B。
     * 地址B就是最终的操作数地址。
     * 这种寻址方式的一个典型应用是在处理结构体数组时，可以将结构体数组的基地址存放在内存中，用Y寄存器作为索引，通过IZY寻址方式快速访问结构体数组的任意元素。
     *
     * @return
     */
    public byte AM_IZY() {
        int d = fromBusRead(pc);
        pc++;
        int low = fromBusRead(d);
        int high = fromBusRead((d + 1) & 0xFF);
        address_abs = (high << 8) | low;
        address_abs = (address_abs + y) & 0xFFFF;
        if ((address_abs & 0xFF00) != (high << 8)) {//偏移地址如果跨页，则CPU时钟增长1个时钟周期
            return 1;
        }
        return 0;
    }

    //寻址模式部分结束------------------------------------------------------------------------------------------------------

    //56个指令操作--------------------------------------------------------------------------------

    /**
     * Add with Carry
     * 带进位的加法
     * <p>
     * 影响STATUS：FLAG_N,FLAG_V,FLAG_Z,FLAG_C
     *
     * @return
     */
    public byte ADC() {
        int fetched = fetch();
        int temp = a + fetched + getStatus(FLAG_C);
        setStatus(FLAG_C, temp > 0xFF);//是否发生进位
        setStatus(FLAG_V, ((~((a & 0xFF) ^ (fetched & 0xFF)) & ((a & 0xFF) ^ (temp & 0xFF))) & 0x0080) != 0);//是否发生溢出
        setStatus(FLAG_Z, (temp & 0xFF) == 0);//结果是否为0
        setStatus(FLAG_N, (temp & 0x80) == 0x80);//结果是否为负数
        a = temp & 0x00FF;//将计算结果存入累加寄存器
        return 0;
    }

    /**
     * AND指令的详细步骤：
     *
     * 读取操作数。操作数的来源取决于指令的寻址模式，可能是立即数、内存地址等。
     *
     * 将累加器的值和操作数的值进行逻辑与运算。
     *
     * 将结果存回累加器。
     *
     * 根据结果更新处理器状态寄存器（P寄存器）的标志位。包括零标志（Zero Flag）和负标志（Negative Flag）。
     *
     * 零标志：如果结果为0，那么零标志会被设置。
     *
     * 负标志：如果结果的最高位（也就是符号位）为1，那么负标志会被设置。
     * @return
     */
    public byte AND() {
        int fetched = fetch();//获取寻址模式要求的值
        a &= fetched;//与累加寄存器的值进行与等运算
        setStatus(FLAG_Z, a == 0);//运算完如果为0，则状态寄存器ZERO位为1
//        setStatus(FLAG_N,(a&0x80)==0x80);//是否为负数，1字节中最高位是符号位，与运算后如果符号为为1则认为运算为是一个负数
        setStatus(FLAG_N, (a & 0x80) == 0x80);//是否为负数，1字节中最高位是符号位，与运算后如果符号为为1则认为运算为是一个负数
        return 1;//该指令可能由于不同的寻址方式导致增加时钟周期，由于寻址模式在特定条件下需要增加时钟，如果该条指令参考它，则这条指令需要增加一个时钟(参看:clock方法:cycles=cycle1&cycle2)
    }

    /**
     * Arithmetic Shift Left
     * 算术左移
     * A = C <- (A << 1) <- 0
     * 影响状态寄存器:N,Z,C
     *
     * @return
     */
    public byte ASL() {
        int fetched = fetch();//取出操作码
        int temp = (fetched << 1)&0xFF;//操作数左移1位
        setStatus(FLAG_C, (fetched &0x80)==0x80);//是否超过一个字节最大值，发生第9位进位
        setStatus(FLAG_Z, temp == 0);//是否为0
        setStatus(FLAG_N, (temp & 0x80) == 0x80);//转换为1字节是否为负数(最高位是否为1)
        toBusWrite(address_abs, temp);//写入指定内存位置
        return 0;
    }

    public byte ASL_ACC() {
        setStatus(FLAG_C, (a &0x80)==0x80);//是否超过一个字节最大值，发生第9位进位
        a = (a << 1)&0xFF;//操作数左移1位
        setStatus(FLAG_Z, a == 0);//是否为0
        setStatus(FLAG_N, (a & 0x80) == 0x80);//转换为1字节是否为负数(最高位是否为1)
        return 0;
    }


    /**
     * Branch if carry flag clear
     * 如果进位标志清除，则分支
     * <p>
     * if(C == 0) pc = address
     *
     * @return
     */
    public byte BCC() {
        //分支时钟要求:如果跨页则+2个时钟周期，否则+1个
        if (getStatus(FLAG_C) != FLAG_C) {
            cycles++;//分支指令肯定要+1个时钟周期
            address_abs = pc + address_rel;
            //如果计算后的地址跨页，则时钟周期再+1，等于+2个时钟周期
            if ((address_abs & 0xFF00) != (pc & 0xFF00)) {
                cycles++;
            }
            pc = address_abs & 0xFFFF;//将程序计数器地址转为寻址要求的地址
        }
        return 0;
    }

    /**
     * Branch if carry flag se
     * 如果设置了进位标志则分支
     *
     * @return
     */
    public byte BCS() {
        //分支时钟要求:如果跨页则+2个时钟周期，否则+1个
        if (getStatus(FLAG_C) == FLAG_C) {
            cycles++;//分支指令肯定要+1个时钟周期
            address_abs = pc + address_rel;
            //如果计算后的地址跨页，则时钟周期再+1，等于+2个时钟周期
            if ((address_abs & 0xFF00) != (pc & 0xFF00)) {
                cycles++;
            }
            pc = address_abs & 0xFFFF;//将程序计数器地址转为寻址要求的地址
        }
        return 0;
    }

    /**
     * Branch if zero flag set
     * 如果设置了零标志，则分支
     *
     * @return
     */
    public byte BEQ() {
        //分支时钟要求:如果跨页则+2个时钟周期，否则+1个
        if (getStatus(FLAG_Z) == FLAG_Z) {
            cycles++;//分支指令肯定要+1个时钟周期
            address_abs = pc + address_rel;
            //如果计算后的地址跨页，则时钟周期再+1，等于+2个时钟周期
            if ((address_abs & 0xFF00) != (pc & 0xFF00)) {
                cycles++;
            }
            pc = address_abs & 0xFFFF;//将程序计数器地址转为寻址要求的地址
        }
        return 0;
    }

    /**
     * Bit Test
     * 位测试
     * 影响状态寄存器:N,V,Z
     * <p>
     * 设置状态寄存器中的零标志（Z flag）：如果累加器与指定内存位置的AND运算结果为零，则设置Z标志。
     * 这可以用来检查累加器中特定的位是否被清除。
     * <p>
     * 设置状态寄存器中的溢出标志（V flag）和负标志（N flag）：BIT命令还会根据操作数的第6位（溢出位）和第7位（符号位）来
     * 设置处理器的溢出标志和负标志。如果操作数的第6位为1，则设置V标志；如果第7位为1，则设置N标志。
     *
     * @return
     */
    public byte BIT() {
        int fetched = fetch();//取出操作数
        int temp = a & fetched;
        setStatus(FLAG_Z, temp == 0);
        setStatus(FLAG_N, (fetched & 0x80) == 0x80);
        setStatus(FLAG_V, (fetched & 0x40) == 0x40);
        return 0;
    }

    /**
     * Branch if negative flag set
     * if(N == 1) pc = address
     * 在设置了负标志则分支
     *
     * @return
     */
    public byte BMI() {
        //分支时钟要求:如果跨页则+2个时钟周期，否则+1个
        if (getStatus(FLAG_N) == FLAG_N) {
            cycles++;//分支指令肯定要+1个时钟周期
            address_abs = pc + address_rel;
            //如果计算后的地址跨页，则时钟周期再+1，等于+2个时钟周期
            if ((address_abs & 0xFF00) != (pc & 0xFF00)) {
                cycles++;
            }
            pc = address_abs & 0xFFFF;//将程序计数器地址转为寻址要求的地址
        }
        return 0;
    }

    /**
     * Branch if zero flag clear
     * 如果零标志清除，则分支
     *
     * @return
     */
    public byte BNE() {
        //分支时钟要求:如果跨页则+2个时钟周期，否则+1个
        if (getStatus(FLAG_Z) != FLAG_Z) {
            cycles++;//分支指令肯定要+1个时钟周期
            address_abs = pc + address_rel;
            //如果计算后的地址跨页，则时钟周期再+1，等于+2个时钟周期
            if ((address_abs & 0xFF00) != (pc & 0xFF00)) {
                cycles++;
            }
            pc = address_abs & 0xFFFF;//将程序计数器地址转为寻址要求的地址
        }
        return 0;
    }

    /**
     * Branch if negative flag clear
     * if(N == 0) pc = address
     * 如果负标志清除，则分支
     *
     * @return
     */
    public byte BPL() {
        //分支时钟要求:如果跨页则+2个时钟周期，否则+1个
        if (getStatus(FLAG_N) != FLAG_N) {
            cycles++;//分支指令肯定要+1个时钟周期
            address_abs = pc + address_rel;
            //如果计算后的地址跨页，则时钟周期再+1，等于+2个时钟周期
            if ((address_abs & 0xFF00) != (pc & 0xFF00)) {
                cycles++;
            }
            pc = address_abs & 0xFFFF;//将程序计数器地址转为寻址要求的地址
        }
        return 0;
    }

    /**
     * Force an interrupt
     * 强制中断
     * 影响状态寄存器:B
     * <p>
     * BRK（Break）指令是6502微处理器中的一条软件中断指令。它的功能是强制中断当前的程序执行流程，并开始执行一个中断处理程序。
     * BRK指令通常用于调试、异常处理或系统调用。
     * <p>
     * 当执行BRK指令时，6502处理器会进行以下操作：
     * 1:程序计数器（PC）增加1，因为BRK被认为是一个两字节的指令，尽管它实际上只有一个字节。
     * 这是为了与硬件中断保持一致，硬件中断会将下一条指令的地址推送到堆栈。
     * <p>
     * 2:将当前程序计数器的值推送到堆栈。这样做是为了在中断处理完成后能够返回到中断前的程序执行点。
     * <p>
     * 3:将处理器状态寄存器的副本推送到堆栈。这包括各种状态标志，如零标志（Z）、进位标志（C）、中断禁止标志（I）等。
     * <p>
     * 4:设置中断禁止标志（I），以防止进一步的中断发生，除非在中断处理程序中明确允许。
     * <p>
     * 5:加载中断向量地址，通常是固定位置在内存的FFFE和FFFF处，到程序计数器。这个地址指向中断处理程序的起始位置。
     * <p>
     * 6:执行位于中断向量地址的中断处理程序。
     * <p>
     * BRK指令允许软件模拟硬件中断的效果，这在操作系统的系统调用实现或其他需要中断当前流程并跳转到特定服务例程的场景
     * 中非常有用。在中断处理程序执行完毕后，通常会使用RTI（Return from Interrupt）指令来恢复原来的程序执行流程。
     *
     * @return
     */
    public byte BRK() {
        //System.out.println("BRK!!!");
        pc++;
        push((pc >> 8));
        push(pc);
        setStatus(FLAG_B, true);
        setStatus(FLAG_U, true);
        push(status);
        setStatus(FLAG_I, true);

        pc = (fromBusRead(0xFFFF) << 8) | fromBusRead(0xFFFE) ;
        return 0;
    }

    /**
     * Branch if overflow flag clear
     * if(V == 0) pc = address
     * 如果溢出标志清除则分支
     *
     * @return
     */
    public byte BVC() {
        //分支时钟要求:如果跨页则+2个时钟周期，否则+1个
        if (getStatus(FLAG_V) != FLAG_V) {
            cycles++;//分支指令肯定要+1个时钟周期
            address_abs = pc + address_rel;
            //如果计算后的地址跨页，则时钟周期再+1，等于+2个时钟周期
            if ((address_abs & 0xFF00) != (pc & 0xFF00)) {
                cycles++;
            }
            pc = address_abs & 0xFFFF;//将程序计数器地址转为寻址要求的地址
        }
        return 0;
    }

    /**
     * Branch if overflow flag set
     * if(V == 1) pc = address
     * 如果设置了溢出标志则分支
     *
     * @return
     */
    public byte BVS() {
        //分支时钟要求:如果跨页则+2个时钟周期，否则+1个
        if (getStatus(FLAG_V) == FLAG_V) {
            cycles++;//分支指令肯定要+1个时钟周期
            address_abs = pc + address_rel;
            //如果计算后的地址跨页，则时钟周期再+1，等于+2个时钟周期
            if ((address_abs & 0xFF00) != (pc & 0xFF00)) {
                cycles++;
            }
            pc = address_abs & 0xFFFF;//将程序计数器地址转为寻址要求的地址
        }
        return 0;
    }

    /**
     * Clear carry flag
     * 清除进位标志
     *
     * @return
     */
    public byte CLC() {
        setStatus(FLAG_C, false);
        return 0;
    }

    /**
     * Clear decimal mode flag
     * 清除十进制模式标志
     *
     * @return
     */
    public byte CLD() {
        setStatus(FLAG_D, false);
        return 0;
    }

    /**
     * Clear interrupt disable flag
     * 清除中断禁用标志
     *
     * @return
     */
    public byte CLI() {
        setStatus(FLAG_I, false);
        //System.out.println("CLI!!!");
        irqDelayed = true;
        return 0;
    }

    /**
     * Clear overflow flag
     * 清除溢出标志
     *
     * @return
     */
    public byte CLV() {
        setStatus(FLAG_V, false);
        return 0;
    }

    /**
     * 用于比较累加器（A寄存器）中的值与另一个字节值。它将累加器中的值与指定的内存位置中的值进行比较，并设置或清除处理器状态寄存器中的几个标志位，以反映比较的结果。
     * <p>
     * CMP指令会影响以下标志位:
     * 零标志（Z）：如果比较结果相等，即累加器中的值与指定值相同，则设置零标志。
     * 负标志（N）：如果比较结果的最高位（第7位）被设置，则设置负标志，这通常表示比较结果为负数（在使用二进制补码表示时）。
     * 进位标志（C）：如果累加器中的值大于或等于指定值，则设置进位标志。在无符号比较中，进位标志可以被视为“不小于”标志。
     * <p>
     * CMP指令不会改变累加器的内容，只是用于设置状态标志，以便后续的分支指令可以根据这些标志来决定程序的流程。例如，如果程序员想要在累加器的值小于某个数时跳转到另一段代码，他们可以使用CMP指令后跟一个条件分支指令，如BCC（Branch if Carry Clear，如果进位标志清除则分支）或BEQ（Branch if Equal，如果相等则分支）。
     * <p>
     * C <- A >= M      Z <- (A - M) == 0
     * <p>
     * 影响状态寄存器: N, C, Z
     *
     * @return
     */
    public byte CMP() {
        //取出操作数
        int fetched = fetch();
        int temp = (a-fetched) & 0xFF;//累加器的值与操作数减法操作
        setStatus(FLAG_C, a >= fetched);
        setStatus(FLAG_Z, temp == 0);
        setStatus(FLAG_N, (temp & 0x80) == 0x80);
        return 1;
    }

    /**
     * CPX指令用于比较X寄存器中的值与另一个值。这个比较是通过从X寄存器的值中减去给定的值来完成的，
     * 但实际上X寄存器的内容并不会改变。CPX指令的结果用于设置或清除处理器的状态标志，
     * 这样就可以根据比较的结果来进行条件分支。
     * <p>
     * C <- X >= M      Z <- (X - M) == 0
     * 影响状态寄存器: N, C, Z
     *
     * @return
     */
    public byte CPX() {
        //取出操作数
        int fetched = fetch();
        int temp = (x - fetched) & 0xFF;
        setStatus(FLAG_C, x >= fetched);
        setStatus(FLAG_Z, temp == 0);
        setStatus(FLAG_N, (temp & 0x80) == 0x80);
        return 0;
    }

    public byte CPY() {
        //取出操作数
        int fetched = fetch();
        int temp = (y - fetched) & 0xFF;
        setStatus(FLAG_C, y >= fetched);
        setStatus(FLAG_Z, temp == 0);
        setStatus(FLAG_N, (temp & 0x80) == 0x80);
        return 0;
    }

    /**
     * DEC 指令用于递减内存中某个地址的值。这意味着它会将指定内存位置的值减少1。
     * DEC 指令不直接作用于寄存器，而是作用于内存。执行此指令后，处理器的零标志（Z）和负标志（N）会根据结果进行更新。
     *
     * @return
     */
    public byte DEC() {
        //获取操作数
        int temp = (fetch() - 1) & 0xFF;//操作数-1
        toBusWrite(address_abs, temp);//将计算结果设置到内存位置
        setStatus(FLAG_Z, temp == 0);
        setStatus(FLAG_N, (temp & 0x80) == 0x80);
        return 0;
    }

    /**
     * Decrement the X register
     * 减少 X 寄存器
     *
     * @return
     */
    public byte DEX() {
        x = (x - 1) & 0xFF;
        setStatus(FLAG_Z, x == 0);
        setStatus(FLAG_N, (x & 0x80) == 0x80);
        return 0;
    }

    public byte DEY() {
        y = (y - 1) & 0xFF;
        setStatus(FLAG_Z, y == 0);
        setStatus(FLAG_N, (y & 0x80) == 0x80);
        return 0;
    }

    /**
     * EOR（Exclusive OR）指令是一个逻辑操作，它对累加器（A）中的值和内存中的一个值执行异或操作。
     * 异或操作的结果是，如果两个比较的位不同，则结果位为1；如果相同，则结果位为0。
     * <p>
     * 逻辑
     * A = A xor M
     *
     * @return
     */
    public byte EOR() {
        //获取操作数
        int fetched = fetch();
        a = (a ^ fetched) & 0xFF;
        setStatus(FLAG_Z, a == 0);
        setStatus(FLAG_N, (a & 0x80) == 0x80);
        return 0;
    }

    /**
     * Increment a memory location
     * 自增address_abs位置的值
     * <p>
     * 影响状态寄存器:Z,N
     *
     * @return
     */
    public byte INC() {
        //获取操作数
        int temp = (fetch() + 1)&0xFF;
        toBusWrite(address_abs, temp);
        setStatus(FLAG_Z, temp == 0);
        setStatus(FLAG_N, (temp & 0x80) == 0x80);
        return 0;
    }

    public byte INX() {
        x = (x + 1) & 0xFF;
        setStatus(FLAG_Z, x == 0);
        setStatus(FLAG_N, (x & 0x80) == 0x80);
        return 0;
    }

    public byte INY() {
        y = (y + 1) & 0xFF;
        setStatus(FLAG_Z, y == 0);
        setStatus(FLAG_N, (y & 0x80) == 0x80);
        return 0;
    }

    /**
     * Jump to another location
     * 跳转到另一个位置
     * JMP 指令用于无条件跳转到指定的内存地址。这个指令没有直接与“智力”相关，而是一个控制流指令，允许程序跳转到代码的不同部分
     *
     * @return
     */
    public byte JMP() {
        pc = address_abs;
        return 0;
    }

    /**
     * Jump to a subroutine
     * 跳转到子程序
     * <p>
     * 将当前的程序计数器（PC）值减1后的结果压入堆栈。这是因为在子程序执行完毕后，处理器需要知道从哪里继续执行，所以需要将返回地址保存在堆栈中。这里之所以要减1，是因为6502的RTS指令在从子程序返回时会自动将程序计数器（PC）增加1。
     * 将程序计数器（PC）设置为JSR指令后面的两个字节表示的地址，从而跳转到子程序的位置。
     *
     * @return
     */
    public byte JSR() {
        pc--;
        push(pc >> 8);
        push(pc);
        pc = address_abs;
        return 0;
    }

    /**
     * 加载操作数到累加寄存器
     * 影响状态寄存器:Z,N
     *
     * @return
     */
    public byte LDA() {
        a = fetch();
        setStatus(FLAG_Z, a == 0);
        setStatus(FLAG_N, (a & 0x80) == 0x80);
        return 1;
    }

    public byte LDX() {
        x = fetch();
        setStatus(FLAG_Z, x == 0);
        setStatus(FLAG_N, (x & 0x80) == 0x80);
        return 1;
    }

    public byte LDY() {
        y = fetch();
        setStatus(FLAG_Z, y == 0);
        setStatus(FLAG_N, (y & 0x80) == 0x80);
        return 1;
    }

    /**
     * LSR（Logical Shift Right）命令是一个逻辑右移指令，它将寄存器或内存中的值向右移动一位。在这个过程中，最左边的位（最高位）被置为0，而最右边的位（最低位）被移出并存储到进位标志（Carry flag）中。LSR指令不会改变符号位，因为6502处理器中的数值是无符号的。
     * <p>
     * LSR可以作用于累加器（A寄存器）或内存中的某个地址。当作用于累加器时，指令格式为LSR A，而作用于内存时，可以使用不同的寻址模式，例如LSR $C000（绝对寻址）或LSR $20,X（零页X寄存器偏移寻址）。
     * <p>
     * LSR指令的影响如下：
     * <p>
     * 逻辑右移：被操作的数值向右移动一位。
     * 设置进位标志：原数值的最低位移入进位标志（C）。
     * 设置零标志：如果结果为零，则设置零标志（Z）。
     * 清除负标志：由于逻辑右移不影响符号位（6502中的数值是无符号的），负标志（N）总是被清除。
     * 清除溢出标志：溢出标志（V）不受LSR指令影响，通常会被清除。
     * 例如，如果累加器A中的值是%10101010（十进制的170），执行LSR A后，累加器A中的值将变为%01010101（十进制的85），进位标志C将被设置为1，因为原来A的最低位是1。如果结果是%00000000，则零标志Z也会被设置。
     * <p>
     * LSR指令在编程中常用于位操作，比如将数值除以2（因为向右移动一位在二进制中相当于除以2），或者在处理位字段和标志时。
     *
     * @return 影响状态寄存器：N,Z,C
     */
    public byte LSR() {
        int fetched = fetch();
        int temp = (fetched >> 1) & 0xFF;
        setStatus(FLAG_C, (fetched & 0x01) == 1);
        setStatus(FLAG_Z, temp == 0);
        setStatus(FLAG_N, false);
        toBusWrite(address_abs, temp);
        return 0;
    }
    public byte LSR_ACC() {//针对AM_ACC寻址
        setStatus(FLAG_C, (a & 0x01) == 1);
        a = a >> 1;
        setStatus(FLAG_Z, a == 0);
        setStatus(FLAG_N, false);
        return 0;
    }


    /**
     * No Operation
     * 无操作
     * <p>
     * NOP（No Operation）指令用于执行一个空操作，即不对处理器的状态（寄存器、内存、标志等）产生任何影响的操作。
     * 6502的NOP指令有多个不同的操作码，因为有些非官方的NOP指令实际上是未使用的操作码，它们没有分配给特定的操作，
     * 但当执行时，它们会被处理器解释为NOP。
     *
     * @return
     */
    public byte NOP() {
        switch (opcode) {
            case 0x1C:
            case 0x3C:
            case 0x5C:
            case 0x7C:
            case 0xDC:
            case 0xFC:
                return 1;
        }
        return 0;
    }

    /**
     * Logical Inclusive OR
     * 逻辑全包 OR
     * A = A | M
     * <p>
     * 影响状态寄存器:N,Z
     * <p>
     * ORA（OR Accumulator）指令用于执行逻辑“或”（OR）操作，
     * 将累加器（A）寄存器中的值与指定内存位置的值进行按位OR运算。结果替换累加器中原有的值。
     *
     * @return
     */
    public byte ORA() {
        a = a | fetch();
        setStatus(FLAG_Z, a == 0);
        setStatus(FLAG_N, (a & 0x80) == 0x80);//查看是否为负数
        return 0;
    }

    /**
     * Push accumulator on stack
     * 将累加器推入堆栈
     *
     * @return
     */
    public byte PHA() {
        push(a);
        return 0;
    }

    /**
     * Push processor status on stack
     * 将处理器状态推入堆栈
     *
     * 当执行PHP指令时，处理器会做以下几件事情：
     *
     * 将状态寄存器P的内容复制一份。
     * 在复制的状态寄存器内容中，将B位（Break）和U位（Unused）设为1。
     * 将复制并修改后的状态寄存器内容压入堆栈。
     *
     * @return
     */
    public byte PHP() {
        push((status | FLAG_B | FLAG_U));
//        setStatus(FLAG_B, false);
//        setStatus(FLAG_U, false);
        //System.out.println("PHP!!!");
        irqDelayed = true;
        return 0;
    }

    /**
     * Pull accumulator from stack
     * 从堆栈中拉出到累加器
     * 影响状态寄存器:N,Z
     *
     * @return
     */
    public byte PLA() {
        a = pop();
        setStatus(FLAG_Z, a == 0);//是否为0
        setStatus(FLAG_N, (a & 0x80) == 0x80);//是否为负数
        return 0;
    }

    /**
     * Pull processor status from stack
     * 从堆栈中拉出处理器状态
     * 影响状态寄存器:全部
     *
     * @return
     */
    public byte PLP() {
        status = pop();//直接将栈顶值赋值到状态寄存器
//        int t_status = pop();//直接将栈顶值赋值到状态寄存器
//        boolean b = getStatus(FLAG_B)==FLAG_B;
//        boolean u = getStatus(FLAG_U)==FLAG_U;
//        status = t_status;
//        setStatus(FLAG_B, b);
//        setStatus(FLAG_U, u);
//        setStatus(FLAG_U, true);
        irqDelayed = true;
        //System.out.println("PLP!!!");
        return 0;
    }

    /**
     * Rotate Left
     * 左旋转
     * ROL指令代表"Rotate Left"，即向左旋转。这个指令将指定的字节（通常是累加器A或内存位置）中的所有位向左移动一位。
     * 最左边的位（第7位）移入进位标志（Carry Flag），而进位标志的旧值则移入最右边的位（第0位）。
     *
     * @return
     */
    public byte ROL() {
        //获取操作数
        int fetched = fetch();
        int temp = ((fetched << 1) | getStatus(FLAG_C))&0xFF;
        setStatus(FLAG_C, (fetched & 0x80) == 0x80);
        setStatus(FLAG_Z, temp  == 0);
        setStatus(FLAG_N, (temp & 0x80) == 0x80);
        toBusWrite(address_abs, temp);
        return 0;
    }
    public byte ROL_ACC() {
        //获取操作数
        int temp = ((a << 1) | getStatus(FLAG_C)) & 0xFF;
        setStatus(FLAG_C, (a & 0x80) == 0x80);
        setStatus(FLAG_Z, temp  == 0);
        setStatus(FLAG_N, (temp & 0x80) == 0x80);
        a = temp;
        return 0;
    }

    public byte ROR() {
        //获取操作数
        int fetched = fetch();
        int temp = (fetched >> 1) | (getStatus(FLAG_C) << 7);
        setStatus(FLAG_C, (fetched & 0x01) == 1);
        setStatus(FLAG_Z, temp == 0);
        setStatus(FLAG_N, (temp & 0x80) == 0x80);
        toBusWrite(address_abs, temp );
        return 0;
    }
    public byte ROR_ACC() {//针对ACC寻址
        int temp = (a >> 1) | (getStatus(FLAG_C) << 7);
        setStatus(FLAG_C, (a & 0x01) == 1);
        setStatus(FLAG_Z, temp == 0);
        setStatus(FLAG_N, (temp & 0x80) == 0x80);
        a = temp;
        return 0;
    }

    /**
     * Return from Interrupt
     * 从中断返回
     *
     * 从堆栈中弹出状态寄存器P的内容，并恢复到状态寄存器P中。这和执行PLP指令的操作类似，但是RTI会恢复所有的状态位，包括I位（Interrupt Disable）。
     * 从堆栈中弹出两个字节，并将这两个字节的值作为新的程序计数器（PC）的值，从而恢复到中断前的代码执行位置。
     *
     * @return
     */
    public byte RTI() {
        status = pop();
        pc = pop();
        pc = (pop()<< 8) | pc;
        return 0;
    }


    /**
     * RTS指令代表 "Return from Subroutine"。这个指令用于从一个子程序返回到主程序。
     * 当执行一个子程序调用时，使用JSR（Jump to SubRoutine）指令，6502会将当前程序计数器（PC）的地址（减去一）
     * 压入栈中，然后跳转到指定的子程序地址。当子程序执行完毕后，使用RTS指令可以从栈中弹出之前保存的程序计数器的值，
     * 并将控制权返回给调用它的程序。
     * <p>
     * RTS指令的操作步骤如下：
     * 从栈中弹出两个字节（低位字节先弹出，然后是高位字节），这两个字节组合起来形成返回地址。
     * 程序计数器（PC）被设置为这个返回地址加一，因为JSR指令在压栈时存储的是调用地址减去一。
     * 执行继续从返回地址的下一条指令开始。
     *
     * @return
     */
    public byte RTS() {
        pc = pop();
        pc = (pop() << 8) | pc;
        pc++;
        return 0;
    }

    /**
     * Subtract with Carry
     * 带进位的减法
     * <p>
     * 影响STATUS:N,V,Z,C
     *
     * @return
     */
    public byte SBC() {
        int value = fetch() ^ 0xFF;
        int temp = a + value + getStatus(FLAG_C);
        setStatus(FLAG_C, (temp & 0xFF00) != 0);
        setStatus(FLAG_Z, (temp & 0xFF) == 0);
        setStatus(FLAG_V, ((temp ^ a) & (temp ^ value) & 0x80) != 0);
        setStatus(FLAG_N, (temp & 0x80) == 0x80);
        a = temp & 0xFF;
        return 1;
    }


    /**
     * Set carry flag
     * 设置进位标志
     *
     * @return
     */
    public byte SEC() {
        setStatus(FLAG_C, true);
        return 0;
    }

    /**
     * Set decimal mode flag
     * 设置十进制模式标志
     *
     * @return
     */
    public byte SED() {
        setStatus(FLAG_D, true);
        return 0;
    }

    /**
     * 设置中断信号
     *
     * @return
     */
    public byte SEI() {
        setStatus(FLAG_I, true);
        //System.out.println("SEI!!!");
        return 0;
    }

    /**
     * Store Accumulator at Address
     * 存储累加器
     *
     * @return
     */
    public byte STA() {
        toBusWrite(address_abs, a);
        return 0;
    }

    /**
     * Store X Register at Address
     *
     * @return
     */
    public byte STX() {
        toBusWrite(address_abs, x);
        return 0;
    }

    /**
     * Store Y Register at Address
     *
     * @return
     */
    public byte STY() {
        toBusWrite(address_abs, y);
        return 0;
    }

    /**
     * Transfer Accumulator to X Register
     * 将累加寄存器A的值赋值给寄存器X
     * x = a
     * 影响状态寄存器:Z,N
     *
     * @return
     */
    public byte TAX() {
        x = a;
        setStatus(FLAG_Z, x  == 0);
        setStatus(FLAG_N, (x & 0x80) == 0x80);
        return 0;
    }

    /**
     * Transfer Accumulator to Y Register
     *
     * @return
     */
    public byte TAY() {
        y = a;
        setStatus(FLAG_Z, y == 0);
        setStatus(FLAG_N, (y & 0x80) == 0x80);
        return 0;
    }

    /**
     * Transfer Stack Pointer to X Register
     * X = stack pointer
     * 影响状态寄存器:Z,N
     *
     * @return
     */
    public byte TSX() {
        x = stack_point;
        setStatus(FLAG_Z, x == 0);
        setStatus(FLAG_N, (x & 0x80) == 0x80);
        return 0;
    }

    /**
     * Transfer X Register to Accumulator
     * A = X
     *
     * @return
     */
    public byte TXA() {
        a = x;
        setStatus(FLAG_Z, a == 0);
        setStatus(FLAG_N, (a & 0x80) == 0x80);
        return 0;
    }

    /**
     * Transfer X Register to Stack Pointer
     * stack pointer = X
     *
     * @return
     */
    public byte TXS() {
        stack_point = x;
        return 0;
    }

    /**
     * Transfer Y Register to Accumulator
     * A = Y
     *
     * @return
     */
    public byte TYA() {
        a = y;
        setStatus(FLAG_Z, a == 0);
        setStatus(FLAG_N, (a & 0x80) == 0x80);
        return 0;
    }

    public byte XXX() {
        return 0;
    }//非法指令


    private void push(int data){
        toBusWrite(0x0100 + stack_point, data);
        stack_point--;
        stack_point&=0xFF;
    }
    public int pop(){
        stack_point++;
        stack_point&=0xFF;
        return fromBusRead(0x0100 + stack_point);
    }

    //指令操作部分结束--------------------------------------------------------------------------------


    //一条指令
    private class Instruction implements Serializable {
        private String name;
        private String addressModeName;
        private Instruct operate;
        private AddressMode addressMode;
        private int cycles;

        public Instruction(String name, Instruct operate, String addressModeName, AddressMode addressMode, int cycles) {
            this.name = name;
            this.operate = operate;
            this.addressModeName = addressModeName;
            this.addressMode = addressMode;
            this.cycles = cycles;
        }
    }

    //lambda表达式表示需要执行某条指令
    @FunctionalInterface
    public interface Instruct extends Serializable {
        public byte execute();
    }

    @FunctionalInterface
    //lambda表达式表示使用某个寻址模式
    public interface AddressMode extends Serializable {
        public byte execute();
    }

    //保存所有指令，指令下表应当是该指令的2进制转换的整数对应
    private List<Instruction> instructionList = new ArrayList<>();

    /**
     * 初始化指令集
     */
    private void initInstructionList() {
        instructionList.add(new Instruction("BRK", this::BRK, AM_IMP, this::AM_IMP, 7));//#00
        instructionList.add(new Instruction("ORA", this::ORA, AM_IZX, this::AM_IZX, 6));//#01
        addIllegalInstruct(AM_IMP,this::AM_IMP);//#02
        addIllegalInstruct(AM_IZX,this::AM_IZX);//#03
        addIllegalInstruct(AM_ZP0,this::AM_ZP0);//#04
        instructionList.add(new Instruction("ORA", this::ORA, AM_ZP0, this::AM_ZP0, 3));//#05
        instructionList.add(new Instruction("ASL", this::ASL, AM_ZP0, this::AM_ZP0, 5));//#06
        addIllegalInstruct(AM_ZP0,this::AM_ZP0);//#07
        instructionList.add(new Instruction("PHP", this::PHP, AM_IMP, this::AM_IMP, 3));//#08
        instructionList.add(new Instruction("ORA", this::ORA, AM_IMM, this::AM_IMM, 2));//#09
        instructionList.add(new Instruction("ASL", this::ASL_ACC, AM_ACC, this::AM_ACC, 2));//#0A
        addIllegalInstruct(AM_IMM,this::AM_IMM);//#0B
        addIllegalInstruct(AM_ABS,this::AM_ABS);//#0C
        instructionList.add(new Instruction("ORA", this::ORA, AM_ABS, this::AM_ABS, 4));//#0D
        instructionList.add(new Instruction("ASL", this::ASL, AM_ABS, this::AM_ABS, 6));//#0E
        addIllegalInstruct(AM_ABS,this::AM_ABS);//#0F


        instructionList.add(new Instruction("BPL", this::BPL, AM_REL, this::AM_REL, 2));//#10   2''
        instructionList.add(new Instruction("ORA", this::ORA, AM_IZY, this::AM_IZY, 5));//#11   5'
        addIllegalInstruct(AM_IMP,this::AM_IMP);//#12
        addIllegalInstruct(AM_IZY,this::AM_IZY);//#13
        addIllegalInstruct(AM_ZPX,this::AM_ZPX);//#14
        instructionList.add(new Instruction("ORA", this::ORA, AM_ZPX, this::AM_ZPX, 4));//#15
        instructionList.add(new Instruction("ASL", this::ASL, AM_ZPX, this::AM_ZPX, 6));//#16
        addIllegalInstruct(AM_ZPX,this::AM_ZPX);//#17
        instructionList.add(new Instruction("CLC", this::CLC, AM_IMP, this::AM_IMP, 2));//#18
        instructionList.add(new Instruction("ORA", this::ORA, AM_ABY, this::AM_ABY, 4));//#19 4'
        addIllegalInstruct(AM_IMP,this::AM_IMP);//#1A
        addIllegalInstruct(AM_ABY,this::AM_ABY);//#1B
        addIllegalInstruct(AM_ABX,this::AM_ABX);//#1C
        instructionList.add(new Instruction("ORA", this::ORA, AM_ABX, this::AM_ABX, 4));//#1D 4'
        instructionList.add(new Instruction("ASL", this::ASL, AM_ABX, this::AM_ABX, 7));//#1E
        addIllegalInstruct(AM_ABX,this::AM_ABX);//#1F

        instructionList.add(new Instruction("JSR", this::JSR, AM_ABS, this::AM_ABS, 6));//#20
        instructionList.add(new Instruction("AND", this::AND, AM_IZX, this::AM_IZX, 6));//#21
        addIllegalInstruct(AM_IMP,this::AM_IMP);//#22
        addIllegalInstruct(AM_IZX,this::AM_IZX);//#23
        instructionList.add(new Instruction("BIT", this::BIT, AM_ZP0, this::AM_ZP0, 3));//#24
        instructionList.add(new Instruction("AND", this::AND, AM_ZP0, this::AM_ZP0, 3));//#25
        instructionList.add(new Instruction("ROL", this::ROL, AM_ZP0, this::AM_ZP0, 5));//#26
        addIllegalInstruct(AM_ZP0,this::AM_ZP0);//#27
        instructionList.add(new Instruction("PLP", this::PLP, AM_IMP, this::AM_IMP, 4));//#28
        instructionList.add(new Instruction("AND", this::AND, AM_IMM, this::AM_IMM, 2));//#29
        instructionList.add(new Instruction("ROL", this::ROL_ACC, AM_ACC, this::AM_ACC, 2));//#2A
        addIllegalInstruct(AM_IMM,this::AM_IMM);//#2B
        instructionList.add(new Instruction("BIT", this::BIT, AM_ABS, this::AM_ABS, 4));//#2C
        instructionList.add(new Instruction("AND", this::AND, AM_ABS, this::AM_ABS, 4));//#2D
        instructionList.add(new Instruction("ROL", this::ROL, AM_ABS, this::AM_ABS, 6));//#2E
        addIllegalInstruct(AM_ABS,this::AM_ABS);//#2F

        instructionList.add(new Instruction("BMI", this::BMI, AM_REL, this::AM_REL, 2));//#30  2''
        instructionList.add(new Instruction("AND", this::AND, AM_IZY, this::AM_IZY, 5));//#31  5'
        addIllegalInstruct(AM_IMP,this::AM_IMP);//#32
        addIllegalInstruct(AM_IZY,this::AM_IZY);//#33
        addIllegalInstruct(AM_ZPX,this::AM_ZPX);//#34
        instructionList.add(new Instruction("AND", this::AND, AM_ZPX, this::AM_ZPX, 4));//#35
        instructionList.add(new Instruction("ROL", this::ROL, AM_ZPX, this::AM_ZPX, 6));//#36
        addIllegalInstruct(AM_ZPX,this::AM_ZPX);//#37
        instructionList.add(new Instruction("SEC", this::SEC, AM_IMP, this::AM_IMP, 2));//#38
        instructionList.add(new Instruction("AND", this::AND, AM_ABY, this::AM_ABY, 4));//#39   4'
        addIllegalInstruct(AM_IMP,this::AM_IMP);//#3A
        addIllegalInstruct(AM_ABY,this::AM_ABY);//#3B
        addIllegalInstruct(AM_ABX,this::AM_ABX);//#3C
        instructionList.add(new Instruction("AND", this::AND, AM_ABX, this::AM_ABX, 4));//#3D   4'
        instructionList.add(new Instruction("ROL", this::ROL, AM_ABX, this::AM_ABX, 7));//#3E
        addIllegalInstruct(AM_ABX,this::AM_ABX);//#3F

        instructionList.add(new Instruction("RTI", this::RTI, AM_IMP, this::AM_IMP, 6));//#40
        instructionList.add(new Instruction("EOR", this::EOR, AM_IZX, this::AM_IZX, 6));//#41
        addIllegalInstruct(AM_IMP,this::AM_IMP);//#42
        addIllegalInstruct(AM_IZX,this::AM_IZX);//#43
        addIllegalInstruct(AM_ZP0,this::AM_ZP0);//#44
        instructionList.add(new Instruction("EOR", this::EOR, AM_ZP0, this::AM_ZP0, 3));//#45
        instructionList.add(new Instruction("LSR", this::LSR, AM_ZP0, this::AM_ZP0, 5));//#46
        addIllegalInstruct(AM_ZP0,this::AM_ZP0);//#47
        instructionList.add(new Instruction("PHA", this::PHA, AM_IMP, this::AM_IMP, 3));//$48
        instructionList.add(new Instruction("EOR", this::EOR, AM_IMM, this::AM_IMM, 2));//$49
        instructionList.add(new Instruction("LSR", this::LSR_ACC, AM_ACC, this::AM_ACC, 2));//$4A
        addIllegalInstruct(AM_IMM,this::AM_IMM);//#4B
        instructionList.add(new Instruction("JMP", this::JMP, AM_ABS, this::AM_ABS, 3));//#4C
        instructionList.add(new Instruction("EOR", this::EOR, AM_ABS, this::AM_ABS, 4));//#4D
        instructionList.add(new Instruction("LSR", this::LSR, AM_ABS, this::AM_ABS, 6));//#4E
        addIllegalInstruct(AM_ABS,this::AM_ABS);//#4F

        instructionList.add(new Instruction("BVC", this::BVC, AM_REL, this::AM_REL, 2));//#50   2''
        instructionList.add(new Instruction("EOR", this::EOR, AM_IZY, this::AM_IZY, 5));//#51   5'
        addIllegalInstruct(AM_IMP,this::AM_IMP);//#52
        addIllegalInstruct(AM_IZY,this::AM_IZY);//#53
        addIllegalInstruct(AM_ZPX,this::AM_ZPX);//#54
        instructionList.add(new Instruction("EOR", this::EOR, AM_ZPX, this::AM_ZPX, 4));//#55
        instructionList.add(new Instruction("LSR", this::LSR, AM_ZPX, this::AM_ZPX, 6));//#56
        addIllegalInstruct(AM_ZPX,this::AM_ZPX);//#57
        instructionList.add(new Instruction("CLI", this::CLI, AM_IMP, this::AM_IMP, 2));//#58
        instructionList.add(new Instruction("EOR", this::EOR, AM_ABY, this::AM_ABY, 4));//#59   4'
        addIllegalInstruct(AM_IMP,this::AM_IMP);//#5A
        addIllegalInstruct(AM_ABY,this::AM_ABY);//#5B
        addIllegalInstruct(AM_ABX,this::AM_ABX);//#5C
        instructionList.add(new Instruction("EOR", this::EOR, AM_ABX, this::AM_ABX, 4));//#5D   4‘
        instructionList.add(new Instruction("LSR", this::LSR, AM_ABX, this::AM_ABX, 7));//#5E
        addIllegalInstruct(AM_ABX,this::AM_ABX);//#5F

        instructionList.add(new Instruction("RTS", this::RTS, AM_IMP, this::AM_IMP, 6));//#60
        instructionList.add(new Instruction("ADC", this::ADC, AM_IZX, this::AM_IZX, 6));//#61
        addIllegalInstruct(AM_IMP,this::AM_IMP);//#62
        addIllegalInstruct(AM_IZX,this::AM_IZX);//#63
        addIllegalInstruct(AM_ZP0,this::AM_ZP0);//#64
        instructionList.add(new Instruction("ADC", this::ADC, AM_ZP0, this::AM_ZP0, 3));//#65
        instructionList.add(new Instruction("ROR", this::ROR, AM_ZP0, this::AM_ZP0, 5));//#66
        addIllegalInstruct(AM_ZP0,this::AM_ZP0);//#67
        instructionList.add(new Instruction("PLA", this::PLA, AM_IMP, this::AM_IMP, 4));//#68
        instructionList.add(new Instruction("ADC", this::ADC, AM_IMM, this::AM_IMM, 2));//#69
        instructionList.add(new Instruction("ROR", this::ROR_ACC, AM_ACC, this::AM_ACC, 2));//#6A
        addIllegalInstruct(AM_IMM,this::AM_IMM);//#6B
        instructionList.add(new Instruction("JMP", this::JMP, AM_IND, this::AM_IND, 5));//#6C
        instructionList.add(new Instruction("ADC", this::ADC, AM_ABS, this::AM_ABS, 4));//#6D
        instructionList.add(new Instruction("ROR", this::ROR, AM_ABS, this::AM_ABS, 6));//#6E
        addIllegalInstruct(AM_ABS,this::AM_ABS);//#6F

        instructionList.add(new Instruction("BVS", this::BVS, AM_REL, this::AM_REL, 2));//#70    2''
        instructionList.add(new Instruction("ADC", this::ADC, AM_IZY, this::AM_IZY, 5));//#71    5'
        addIllegalInstruct(AM_IMP,this::AM_IMP);//#72
        addIllegalInstruct(AM_IZY,this::AM_IZY);//#73
        addIllegalInstruct(AM_ZPX,this::AM_ZPX);//#74
        instructionList.add(new Instruction("ADC", this::ADC, AM_ZPX, this::AM_ZPX, 4));//#75
        instructionList.add(new Instruction("ROR", this::ROR, AM_ZPX, this::AM_ZPX, 6));//#76
        addIllegalInstruct(AM_ZPX,this::AM_ZPX);//#77
        instructionList.add(new Instruction("SEI", this::SEI, AM_IMP, this::AM_IMP, 2));//#78
        instructionList.add(new Instruction("ADC", this::ADC, AM_ABY, this::AM_ABY, 4));//#79    4'
        addIllegalInstruct(AM_IMP,this::AM_IMP);//#7A
        addIllegalInstruct(AM_ABY,this::AM_ABY);//#7B
        addIllegalInstruct(AM_ABX,this::AM_ABX);//#7C
        instructionList.add(new Instruction("ADC", this::ADC, AM_ABX, this::AM_ABX, 4));//#7D    4'
        instructionList.add(new Instruction("ROR", this::ROR, AM_ABX, this::AM_ABX, 7));//#7E
        addIllegalInstruct(AM_ABX,this::AM_ABX);//#7F

        addIllegalInstruct(AM_IMM,this::AM_IMM);//#80
        instructionList.add(new Instruction("STA", this::STA, AM_IZX, this::AM_IZX, 6));//#81
        addIllegalInstruct(AM_IMM,this::AM_IMM);//#82
        addIllegalInstruct(AM_IZX,this::AM_IZX);//#83
        instructionList.add(new Instruction("STY", this::STY, AM_ZP0, this::AM_ZP0, 3));//#84
        instructionList.add(new Instruction("STA", this::STA, AM_ZP0, this::AM_ZP0, 3));//#85
        instructionList.add(new Instruction("STX", this::STX, AM_ZP0, this::AM_ZP0, 3));//#86
        addIllegalInstruct(AM_ZP0,this::AM_ZP0);//#87
        instructionList.add(new Instruction("DEY", this::DEY, AM_IMP, this::AM_IMP, 2));//#88
        addIllegalInstruct(AM_IMM,this::AM_IMM);//#89
        instructionList.add(new Instruction("TXA", this::TXA, AM_IMP, this::AM_IMP, 2));//#8A
        addIllegalInstruct(AM_IMM,this::AM_IMM);//#8B
        instructionList.add(new Instruction("STY", this::STY, AM_ABS, this::AM_ABS, 4));//#8C
        instructionList.add(new Instruction("STA", this::STA, AM_ABS, this::AM_ABS, 4));//#8D
        instructionList.add(new Instruction("STX", this::STX, AM_ABS, this::AM_ABS, 4));//#8E
        addIllegalInstruct(AM_ABS,this::AM_ABS);//#8F

        instructionList.add(new Instruction("BCC", this::BCC, AM_REL, this::AM_REL, 2));//#90    2''
        instructionList.add(new Instruction("STA", this::STA, AM_IZY, this::AM_IZY, 6));//#91
        addIllegalInstruct(AM_IMP,this::AM_IMP);//#92
        addIllegalInstruct(AM_IZY,this::AM_IZY);//#93
        instructionList.add(new Instruction("STY", this::STY, AM_ZPX, this::AM_ZPX, 4));//94
        instructionList.add(new Instruction("STA", this::STA, AM_ZPX, this::AM_ZPX, 4));//95
        instructionList.add(new Instruction("STX", this::STX, AM_ZPY, this::AM_ZPY, 4));//96
        addIllegalInstruct(AM_ZPY,this::AM_ZPY);//#97
        instructionList.add(new Instruction("TYA", this::TYA, AM_IMP, this::AM_IMP, 2));//#98
        instructionList.add(new Instruction("STA", this::STA, AM_ABY, this::AM_ABY, 5));//#99
        instructionList.add(new Instruction("TXS", this::TXS, AM_IMP, this::AM_IMP, 2));//#9A
        addIllegalInstruct(AM_ABY,this::AM_ABY);//#9B
        addIllegalInstruct(AM_ABX,this::AM_ABX);//#9C
        instructionList.add(new Instruction("STA", this::STA, AM_ABX, this::AM_ABX, 5));//#9D
        addIllegalInstruct(AM_ABY,this::AM_ABY);//#9E
        addIllegalInstruct(AM_ABY,this::AM_ABY);//#9F

        instructionList.add(new Instruction("LDY", this::LDY, AM_IMM, this::AM_IMM, 2));//#A0
        instructionList.add(new Instruction("LDA", this::LDA, AM_IZX, this::AM_IZX, 6));//#A1
        instructionList.add(new Instruction("LDX", this::LDX, AM_IMM, this::AM_IMM, 2));//#A2
        addIllegalInstruct(AM_IZX,this::AM_IZX);//#A3
        instructionList.add(new Instruction("LDY", this::LDY, AM_ZP0, this::AM_ZP0, 3));//#A4
        instructionList.add(new Instruction("LDA", this::LDA, AM_ZP0, this::AM_ZP0, 3));//#A5
        instructionList.add(new Instruction("LDX", this::LDX, AM_ZP0, this::AM_ZP0, 3));//#A6
        addIllegalInstruct(AM_ZP0,this::AM_ZP0);//#A7
        instructionList.add(new Instruction("TAY", this::TAY, AM_IMP, this::AM_IMP, 2));//#A8
        instructionList.add(new Instruction("LDA", this::LDA, AM_IMM, this::AM_IMM, 2));//#A9
        instructionList.add(new Instruction("TAX", this::TAX, AM_IMP, this::AM_IMP, 2));//#AA
        addIllegalInstruct(AM_IMM,this::AM_IMM);//#AB
        instructionList.add(new Instruction("LDY", this::LDY, AM_ABS, this::AM_ABS, 4));//#AC
        instructionList.add(new Instruction("LDA", this::LDA, AM_ABS, this::AM_ABS, 4));//#AD
        instructionList.add(new Instruction("LDX", this::LDX, AM_ABS, this::AM_ABS, 4));//#AE
        addIllegalInstruct(AM_ABS,this::AM_ABS);//#AF

        instructionList.add(new Instruction("BCS", this::BCS, AM_REL, this::AM_REL, 2));//#B0  2''
        instructionList.add(new Instruction("LDA", this::LDA, AM_IZY, this::AM_IZY, 5));//#B1  5'
        addIllegalInstruct(AM_IMP,this::AM_IMP);//#B2
        addIllegalInstruct(AM_IZY,this::AM_IZY);//#B3
        instructionList.add(new Instruction("LDY", this::LDY, AM_ZPX, this::AM_ZPX, 4));//#B4
        instructionList.add(new Instruction("LDA", this::LDA, AM_ZPX, this::AM_ZPX, 4));//#B5
        instructionList.add(new Instruction("LDX", this::LDX, AM_ZPY, this::AM_ZPY, 4));//#B6
        addIllegalInstruct(AM_ZPY,this::AM_ZPY);//#B7
        instructionList.add(new Instruction("CLV", this::CLV, AM_IMP, this::AM_IMP, 2));//#B8
        instructionList.add(new Instruction("LDA", this::LDA, AM_ABY, this::AM_ABY, 4));//#B9   4'
        instructionList.add(new Instruction("TSX", this::TSX, AM_IMP, this::AM_IMP, 2));//#BA
        addIllegalInstruct(AM_ABY,this::AM_ABY);//#BB
        instructionList.add(new Instruction("LDY", this::LDY, AM_ABX, this::AM_ABX, 4));//#BC   4'
        instructionList.add(new Instruction("LDA", this::LDA, AM_ABX, this::AM_ABX, 4));//#BD  4'
        instructionList.add(new Instruction("LDX", this::LDX, AM_ABY, this::AM_ABY, 4));//#BE   4'
        addIllegalInstruct(AM_ABY,this::AM_ABY);//#BF

        instructionList.add(new Instruction("CPY", this::CPY, AM_IMM, this::AM_IMM, 2));//#C0
        instructionList.add(new Instruction("CMP", this::CMP, AM_IZX, this::AM_IZX, 6));//#C1
        addIllegalInstruct(AM_IMM,this::AM_IMM);//#C2
        addIllegalInstruct(AM_IZX,this::AM_IZX);//#C3
        instructionList.add(new Instruction("CPY", this::CPY, AM_ZP0, this::AM_ZP0, 3));//#C4
        instructionList.add(new Instruction("CMP", this::CMP, AM_ZP0, this::AM_ZP0, 3));//#C5
        instructionList.add(new Instruction("DEC", this::DEC, AM_ZP0, this::AM_ZP0, 5));//#C6
        addIllegalInstruct(AM_ZP0,this::AM_ZP0);//#C7
        instructionList.add(new Instruction("INY", this::INY, AM_IMP, this::AM_IMP, 2));//#C8
        instructionList.add(new Instruction("CMP", this::CMP, AM_IMM, this::AM_IMM, 2));//#C9
        instructionList.add(new Instruction("DEX", this::DEX, AM_IMP, this::AM_IMP, 2));//#CA
        addIllegalInstruct(AM_IMM,this::AM_IMM);//#CB
        instructionList.add(new Instruction("CPY", this::CPY, AM_ABS, this::AM_ABS, 4));//#CC
        instructionList.add(new Instruction("CMP", this::CMP, AM_ABS, this::AM_ABS, 4));//#CD
        instructionList.add(new Instruction("DEC", this::DEC, AM_ABS, this::AM_ABS, 6));//#CE
        addIllegalInstruct(AM_ABS,this::AM_ABS);//#CF

        instructionList.add(new Instruction("BNE", this::BNE, AM_REL, this::AM_REL, 2));//#D0   2''
        instructionList.add(new Instruction("CMP", this::CMP, AM_IZY, this::AM_IZY, 5));//#D1   5'
        addIllegalInstruct(AM_IMP,this::AM_IMP);//D2
        addIllegalInstruct(AM_IZY,this::AM_IZY);//D3
        addIllegalInstruct(AM_ZPX,this::AM_ZPX);//D4
        instructionList.add(new Instruction("CMP", this::CMP, AM_ZPX, this::AM_ZPX, 4));//#D5
        instructionList.add(new Instruction("DEC", this::DEC, AM_ZPX, this::AM_ZPX, 6));//#D6
        addIllegalInstruct(AM_ZPX,this::AM_ZPX);//#D7
        instructionList.add(new Instruction("CLD", this::CLD, AM_IMP, this::AM_IMP, 2));//#D8
        instructionList.add(new Instruction("CMP", this::CMP, AM_ABY, this::AM_ABY, 4));//#D9   4'
        addIllegalInstruct(AM_IMP,this::AM_IMP);//#DA
        addIllegalInstruct(AM_ABY,this::AM_ABY);//#DB
        addIllegalInstruct(AM_ABX,this::AM_ABX);//#DC
        instructionList.add(new Instruction("CMP", this::CMP, AM_ABX, this::AM_ABX, 4));//#DD   4'
        instructionList.add(new Instruction("DEC", this::DEC, AM_ABX, this::AM_ABX, 7));//#DE
        addIllegalInstruct(AM_ABX,this::AM_ABX);//#DF

        instructionList.add(new Instruction("CPX", this::CPX, AM_IMM, this::AM_IMM, 2));//#E0
        instructionList.add(new Instruction("SBC", this::SBC, AM_IZX, this::AM_IZX, 6));//#E1
        addIllegalInstruct(AM_IMM,this::AM_IMM);//#E2
        addIllegalInstruct(AM_IZX,this::AM_IZX);//#E3
        instructionList.add(new Instruction("CPX", this::CPX, AM_ZP0, this::AM_ZP0, 3));//#E4
        instructionList.add(new Instruction("SBC", this::SBC, AM_ZP0, this::AM_ZP0, 3));//#E5
        instructionList.add(new Instruction("INC", this::INC, AM_ZP0, this::AM_ZP0, 5));//#E6
        addIllegalInstruct(AM_ZP0,this::AM_ZP0);//#E7
        instructionList.add(new Instruction("INX", this::INX, AM_IMP, this::AM_IMP, 2));//#E8
        instructionList.add(new Instruction("SBC", this::SBC, AM_IMM, this::AM_IMM, 2));//#E9
        instructionList.add(new Instruction("NOP", this::NOP, AM_IMP, this::AM_IMP, 2));//#EA
        addIllegalInstruct(AM_IMM,this::AM_IMM);//#EB
        instructionList.add(new Instruction("CPX", this::CPX, AM_ABS, this::AM_ABS, 4));//#EC
        instructionList.add(new Instruction("SBC", this::SBC, AM_ABS, this::AM_ABS, 4));//#ED
        instructionList.add(new Instruction("INC", this::INC, AM_ABS, this::AM_ABS, 6));//#EE
        addIllegalInstruct(AM_ABS,this::AM_ABS);//#EF

        instructionList.add(new Instruction("BEQ", this::BEQ, AM_REL, this::AM_REL, 2));//#F0  2''
        instructionList.add(new Instruction("SBC", this::SBC, AM_IZY, this::AM_IZY, 5));//#F1  5'
        addIllegalInstruct(AM_IMP,this::AM_IMP);//#F2
        addIllegalInstruct(AM_IZY,this::AM_IZY);//#F2
        addIllegalInstruct(AM_ZPX,this::AM_ZPX);//#F2
        instructionList.add(new Instruction("SBC", this::SBC, AM_ZPX, this::AM_ZPX, 4));//#F5
        instructionList.add(new Instruction("INC", this::INC, AM_ZPX, this::AM_ZPX, 6));//#F6
        addIllegalInstruct(AM_ZPX,this::AM_ZPX);//#F7
        instructionList.add(new Instruction("SED", this::SED, AM_IMP, this::AM_IMP, 2));//#F8
        instructionList.add(new Instruction("SBC", this::SBC, AM_ABY, this::AM_ABY, 4));//#F9   4'
        addIllegalInstruct(AM_IMP,this::AM_IMP);//#FA
        addIllegalInstruct(AM_ABY,this::AM_ABY);//#FB
        addIllegalInstruct(AM_ABX,this::AM_ABX);//#FC
        instructionList.add(new Instruction("SBC", this::SBC, AM_ABX, this::AM_ABX, 4));//#FD   4'
        instructionList.add(new Instruction("INC", this::INC, AM_ABX, this::AM_ABX, 7));//#FE
        addIllegalInstruct(AM_ABX,this::AM_ABX);//#FF
    }

    private void addIllegalInstruct(String addressModeName, AddressMode addressMode) {
//        instructionList.add(new CPU.Instruction("XXX", this::XXX, AM_REL, this::AM_REL, 1));
        instructionList.add(new Instruction("XXX", this::XXX, addressModeName, addressMode, 1));
    }

    private boolean irqDelayed;//irq延迟

}
